package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.product.domain.*;
import com.spzx.product.mapper.ProductDetailsMapper;
import com.spzx.product.mapper.ProductMapper;
import com.spzx.product.mapper.ProductSkuMapper;
import com.spzx.product.mapper.SkuStockMapper;
import com.spzx.product.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {

    @Autowired
    private ProductSkuMapper productSkuMapper;

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private SkuStockMapper skuStockMapper;

    @Autowired
    private ProductDetailsMapper productDetailsMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    //查询商品列表
    @Override
    public List<Product> selectProductList(Product product) {
        List<Product> list = baseMapper.selectProductList(product);
        return list;
    }

    //添加商品
    @Transactional
    @Override
    public int insertProduct(Product product) {
        //0 操作多张表，添加到事务里面
        //1 添加商品基本信息到product表
        baseMapper.insert(product);

        //2 获取前端传递过来商品所有sku列表list集合 productSkuList
        List<ProductSku> productSkuList = product.getProductSkuList();

        //3 把sku列表list集合遍历，得到每个sku对象，把每个sku添加product_sku
        for (int i = 0; i < productSkuList.size(); i++) {
            ProductSku productSku = productSkuList.get(i);
            //设置一些sku其他值
            productSku.setSkuCode(product.getId() + "_" + i);
            productSku.setProductId(product.getId());
            String skuName = product.getName() + " " + productSku.getSkuSpec();
            productSku.setSkuName(skuName);
            //把每个sku添加product_sku表
            productSkuMapper.insert(productSku);

            //4 把每个sku和库存量，添加sku_stock
            SkuStock skuStock = new SkuStock();
            skuStock.setSkuId(productSku.getId());
            skuStock.setTotalNum(productSku.getStockNum());
            skuStock.setLockNum(0);
            skuStock.setAvailableNum(productSku.getStockNum());
            skuStock.setSaleNum(0);
            skuStockMapper.insert(skuStock);
        }

        //5 添加商品product_details
        // 商品图片 image_urls
        // detailsImageUrlList集合 转换字符串
        ProductDetails productDetails = new ProductDetails();

        List<String> detailsImageUrlList = product.getDetailsImageUrlList();
        // [01.jpg,02.jpg]  ==  01.jpg,02.jpg
        productDetails.setProductId(product.getId());

        String detailsImageUrl = String.join(",", detailsImageUrlList);
        productDetails.setImageUrls(detailsImageUrl);

        productDetailsMapper.insert(productDetails);

        return product.getId().intValue();
    }

    //获取商品详细信息
    @Override
    public Product selectProductById(Long id) {
        //1 根据商品id获取商品基本信息，查询product表
        Product product = baseMapper.selectById(id);

        //2 根据商品id获取商品所有sku列表
        LambdaQueryWrapper<ProductSku> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProductSku::getProductId,id);
        List<ProductSku> productSkuList = productSkuMapper.selectList(wrapper);

        //2.1 因为每个sku有对应库存量
        //2.2 查询每个sku库存量封装到每个ProductSku对象里面
        //第一种 productSkuList遍历，得到每个sku，根据每个skuid查询库存表得到库存量，设置对象
//        productSkuList.forEach(productSku -> {
//            Long skuId = productSku.getId();
//            //根据skuId查询库存
//            LambdaQueryWrapper<SkuStock> wrapper1 = new LambdaQueryWrapper<>();
//            wrapper1.eq(SkuStock::getSkuId,skuId);
//            SkuStock skuStock = skuStockMapper.selectOne(wrapper1);
//            Integer totalNum = skuStock.getTotalNum();
//
//            productSku.setStockNum(totalNum);
//        });

        //第二种 stream流数据处理
        //1 从productSkuList获取所有id
        //List<ProductSku>
        List<Long> skuIdList =
                productSkuList.stream().map(ProductSku::getId).collect(Collectors.toList());

        //2 拿着所有skuId集合查询库存表，对应库存信息
        LambdaQueryWrapper<SkuStock> wrapperSkuStock = new LambdaQueryWrapper<>();
        wrapperSkuStock.in(SkuStock::getSkuId,skuIdList);
        List<SkuStock> skuStockList = skuStockMapper.selectList(wrapperSkuStock);

        //3 第二步查询sku所有库存信息list ，转换map集合
        // key:skuId  value:库存量
        Map<Long, Integer> map =
                skuStockList.stream()
                        .collect(Collectors.toMap(SkuStock::getSkuId, SkuStock::getTotalNum));

        //4 把productSkuList遍历，根据skuId到第三步map里面找到对应库存，设置到productSku
        productSkuList.forEach(productSku -> {
            //获取skuId
            Long productSkuId = productSku.getId();
            //根据skuid查询map，获取对应库存量
            Integer num = map.get(productSkuId);
            productSku.setStockNum(num);
        });

        //3 把查询商品所有sku列表封装product里面
        product.setProductSkuList(productSkuList);

        //4 根据商品id获取商品详情数据  图片列表
        LambdaQueryWrapper<ProductDetails> wrapperDetails = new LambdaQueryWrapper<>();
        wrapperDetails.eq(ProductDetails::getProductId,id);
        ProductDetails productDetails = productDetailsMapper.selectOne(wrapperDetails);

        String imageUrls = productDetails.getImageUrls();
        // 01.jpg,02.jpg
        //字符串 imageUrls 转换数组
        String[] split = imageUrls.split(",");
        //把数组转换list集合
        List<String> list = Arrays.asList(split);
        //5 把查询商品图片列表封装product里面
        product.setDetailsImageUrlList(list);

        //6 返回封装完成product对象
        return product;
    }

    //修改
    @Transactional
    @Override
    public int updateProduct(Product product) {
        //修改商品信息
        baseMapper.updateById(product);

        List<ProductSku> productSkuList = product.getProductSkuList();

        productSkuList.forEach(productSku -> {
            //修改商品SKU信息
            productSkuMapper.updateById(productSku);

            //修改商品库存
            SkuStock skuStock =
                    skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>()
                            .eq(SkuStock::getSkuId, productSku.getId()));
            skuStock.setTotalNum(productSku.getStockNum());

            int availableNum = skuStock.getTotalNum() - skuStock.getLockNum();

            skuStock.setAvailableNum(availableNum);
            skuStockMapper.updateById(skuStock);
        });

        //修改商品详细信息
        ProductDetails productDetails =
                productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, product.getId()));
        productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
        productDetailsMapper.updateById(productDetails);
        return 1;
    }

    //删除
    @Transactional
    @Override
    public int deleteProductByIds(Long[] ids) {
        //1 根据商品id删除product
        baseMapper.deleteBatchIds(Arrays.asList(ids));

        //2 删除skuId，删除sku里面库存信息
        //获取商品所有skuId
        LambdaQueryWrapper<ProductSku> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(ProductSku::getProductId,ids);
        List<ProductSku> productSkuList = productSkuMapper.selectList(wrapper);

        //productSkuList  获取skuId值
        List<Long> skuIdList =
                productSkuList.stream().map(ProductSku::getId).collect(Collectors.toList());

        //删除sku对应库存 删除条件 skuId
        LambdaQueryWrapper<SkuStock> wrapperSkuStock = new LambdaQueryWrapper<>();
        wrapperSkuStock.in(SkuStock::getSkuId,skuIdList);
        skuStockMapper.delete(wrapperSkuStock);

        //3 根据商品id删除product_sku
        productSkuMapper
                .delete(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getProductId, ids));

        //4 根据商品id删除详情
        productDetailsMapper
                .delete(new LambdaQueryWrapper<ProductDetails>()
                        .in(ProductDetails::getProductId, ids));
        return 1;
    }

    //查询畅销商品
    @Override
    public List<ProductSku> getTopSale() {
        return productSkuMapper.selectTopSale();
    }

    //远程调用使用，商品列表
    @Override
    public List<ProductSku> selectProductSkuList(SkuQuery skuQuery) {
        return productSkuMapper.selectProductSkuList(skuQuery);
    }

    @Override
    public void unlock(String orderNo) {

    }

    @Override
    public void minus(String orderNo) {

    }

    //接口1 ：根据skuid获取sku数据
    @Override
    public ProductSku getProductSku(Long skuId) {

        ProductSku productSku = productSkuMapper.selectById(skuId);
        return productSku;
    }

    //TODO 添加缓存+分布式锁
    public ProductSku getProductSku1(Long skuId) {

        //构建业务数据Key
        String dataKey = "product:sku:" + skuId;

        //1 根据key查询redis
        ProductSku productSku = (ProductSku)redisTemplate.opsForValue().get(dataKey);

        //2 如果redis可以查询到数据，直接返回
        if(productSku != null) {
            return productSku;
        }

        //3 如果redis查询不到数据，查询mysql
        //3.1 考虑缓存击穿问题，加分布式锁

        //2.1 构建锁key
        String lockKey = "product:sku:lock:" + skuId;
        //2.2 采用UUID作为线程标识
        String lockVal = UUID.randomUUID().toString().replaceAll("-", "");

        //TODO  lockVal
        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, lockVal,
                            10, TimeUnit.SECONDS);
        //3.2 分布式锁加锁成功
        if(ifAbsent) {
            try {
                // 查询mysql，把mysql查询数据放到redis里面
                ProductSku productSku_mysql = productSkuMapper.selectById(skuId);
                //放到redis
                redisTemplate.opsForValue().set(dataKey,productSku_mysql,30,TimeUnit.MINUTES);

                //把数据返回
                return productSku_mysql;
            }finally {
                //释放锁  lua脚本保证原子性
                String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                        "then\n" +
                        "    return redis.call(\"del\",KEYS[1])\n" +
                        "else\n" +
                        "    return 0\n" +
                        "end";
                DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                redisScript.setScriptText(scriptText);
                redisScript.setResultType(Long.class);
                redisTemplate.execute(redisScript, Arrays.asList(lockKey), lockVal);
            }

        } else {
            //3.3 分布式锁加锁不成功，自旋

            try {
                //5.获取锁失败则自旋（业务要求必须执行）
                Thread.sleep(200);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return this.getProductSku1(skuId);
        }
    }


    //接口2：根据商品id获取商品信息
    @Override
    public Product getProduct(Long id) {
        Product product = productMapper.selectById(id);
        return product;
    }

    //接口3：sku最新价格
    @Override
    public SkuPrice getSkuPrice(Long skuId) {
        //根据skuid获取sku信息
        //ProductSku productSku = productSkuMapper.selectById(skuId);

        LambdaQueryWrapper<ProductSku> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProductSku::getId, skuId);
        //指定查询字段  select *
        wrapper.select(ProductSku::getSalePrice, ProductSku::getMarketPrice);
        ProductSku productSku = productSkuMapper.selectOne(wrapper);

        //把sku信息价格封装到SkuPrice
        //productSku.getSalePrice()
        // productSku -- SkuPrice
        SkuPrice skuPrice = new SkuPrice();
        BeanUtils.copyProperties(productSku,skuPrice);
        return skuPrice;
    }

    //接口4：商品id获取详情
    @Override
    public ProductDetails getProductDetails(Long id) {
        LambdaQueryWrapper<ProductDetails> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProductDetails::getProductId,id);
        ProductDetails productDetails = productDetailsMapper.selectOne(wrapper);
        return productDetails;
    }

    //接口5：根据商品id获取商品所有sku规则数据
    @Override
    public Map<String, Long> getSkuSpecValue(Long id) {
        //1 根据商品id获取商品所有sku列表
        LambdaQueryWrapper<ProductSku> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProductSku::getProductId,id);
        wrapper.select(ProductSku::getId, ProductSku::getSkuSpec);
        List<ProductSku> productSkuList = productSkuMapper.selectList(wrapper);

        //2 遍历得到sku列表，得到每个sku规格数据 ，封装map
        // map的key sku规格数据，  value：skuid
        Map<String, Long> map = new HashMap<>();
        productSkuList.forEach(productSku -> {
            map.put(productSku.getSkuSpec(),productSku.getId());
        });

        return map;
    }

    ////接口6：获取sku对应库存数据
    @Override
    public SkuStockVo getSkuStock(Long skuId) {
        LambdaQueryWrapper<SkuStock> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SkuStock::getSkuId,skuId);
        SkuStock skuStock = skuStockMapper.selectOne(wrapper);

        SkuStockVo skuStockVo = new SkuStockVo();
        BeanUtils.copyProperties(skuStock, skuStockVo);
        return skuStockVo;
    }

    //远程调用接口：根据skuid集合查询对应最新价格数据
    @Override
    public List<SkuPrice> getSkuPriceList(List<Long> skuIdList) {
        LambdaQueryWrapper<ProductSku> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(ProductSku::getId,skuIdList);
        List<ProductSku> productSkuList = productSkuMapper.selectList(wrapper);

        // List<ProductSku> -- List<SkuPrice>
        List<SkuPrice> skuPriceList = productSkuList.stream().map(productSku -> {
            SkuPrice skuPrice = new SkuPrice();
            skuPrice.setSkuId(productSku.getId());
            skuPrice.setSalePrice(productSku.getSalePrice());
            skuPrice.setMarketPrice(productSku.getMarketPrice());
            return skuPrice;
        }).collect(Collectors.toList());

        return skuPriceList;
    }

    //远程调用接口：检查购买商品库存是否充足，锁定库存
    @Override
    public String checkAndLock(String orderNo, List<SkuLockVo> skuLockVoList) {

        //skuLockVoList集合
        //需要检查商品skuid和对应数量
        //遍历skuLockVoList得到每个SkuLockVo，根据每个对象skuid和数量查询数据库，sku库存是否充足
        //如果库存充足，设置isHaveStock=true，否则 isHaveStock=false
        skuLockVoList.forEach(skuLockVo -> {
            //根据每个对象skuid和数量查询数据库，sku库存是否充足
            Long skuId = skuLockVo.getSkuId();
            Integer skuNum = skuLockVo.getSkuNum();
            //调用mapper方法查询数据库
            SkuStock skuStock = skuStockMapper.checkStock(skuId,skuNum);
            //判断
            if(skuStock == null) {//库存不足
                skuLockVo.setIsHaveStock(false);
            } else { //库存充足
                skuLockVo.setIsHaveStock(true);
            }
        });

        //购买3个商品，只有三个商品库存都充足，才可以生成订单
        // 如果有任何一个库存不足，提示用户
       // boolean anyMatch = skuLockVoList.stream().anyMatch(skuLockVo -> !skuLockVo.getIsHaveStock());
        // 购买的商品里面，如果有任何一个库存不足
        if(skuLockVoList.stream().anyMatch(skuLockVo -> !skuLockVo.getIsHaveStock())) {
            StringBuffer result = new StringBuffer();
            //获取购买商品集合哪个商品库存不足
            List<SkuLockVo> noHaveStockSkuLockVoList =
                    skuLockVoList.stream().filter(item -> !item.getIsHaveStock()).collect(Collectors.toList());
            for(SkuLockVo noHaveStockSkuLockVo : noHaveStockSkuLockVoList) {
                result.append("商品: " + noHaveStockSkuLockVo.getSkuId() + " 库存不足; ");
            }
            return result.toString();
        } else { //购买商品库存充足
            //锁定库存
            skuLockVoList.forEach(skuLockVo -> {
                int rows = skuStockMapper.lockStock(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
                if(rows == 0) {
                    throw new ServiceException("库存锁定失败");
                }
            });
        }

        String dataKey = "sku:lock:data:" + orderNo;
        // 如果所有商品都锁定成功的情况下，需要缓存锁定信息到redis。以方便将来解锁库存 或者 减库存
        this.redisTemplate.opsForValue().set(dataKey, skuLockVoList);
        return "";
    }

    //还原库存
    @Override
    public void unLockProductStock(String orderNo) {
        //1 从redis里面获取还原库存数据
        String dataKey = "sku:lock:data:" + orderNo;
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>)redisTemplate.opsForValue().get(dataKey);

        //2 从redis获取list集合，遍历list集合，得到每个对象SkuLockVo
        if (CollectionUtils.isEmpty(skuLockVoList)){

            return ;
        }

        skuLockVoList.forEach(skuLockVo -> {
            //3 根据每个对象skuid 和 数量，更新库存数据
            Long skuId = skuLockVo.getSkuId();
            Integer skuNum = skuLockVo.getSkuNum();
            int rows = skuStockMapper.unlockStock(skuId,skuNum);
            if(rows == 0) {
                //解除去重
                this.redisTemplate.delete(dataKey);
                throw new ServiceException("解锁出库失败");
            }
        });

        //4 redis库存数据删除
        this.redisTemplate.delete(dataKey);
    }

    //扣减库存
    @Override
    public void minusStock(String orderNo) {
        //1 从redis获取库存数据，得到list集合
        //1 从redis里面获取还原库存数据
        String dataKey = "sku:lock:data:" + orderNo;
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>)redisTemplate.opsForValue().get(dataKey);

        //2 遍历list集合，得到每个对象，根据每个对象skuid和数量更新库存表
        if (CollectionUtils.isEmpty(skuLockVoList)){
            return ;
        }

        skuLockVoList.forEach(skuLockVo -> {
            Long skuId = skuLockVo.getSkuId();
            Integer skuNum = skuLockVo.getSkuNum();
            int rows = skuStockMapper.minusStock(skuId,skuNum);
            if(rows == 0) {
                //解除去重
                this.redisTemplate.delete(dataKey);
                throw new ServiceException("解锁出库失败");
            }
        });

        //删除redis数据
        redisTemplate.delete(dataKey);
    }

}
